#!/bin/sh
#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#@function: backup oracle db and log

build_backup_datafile_sql()
{
    full_backup_sql=$1
    datafile_info=$2
    has_content="0"
    if [ -z "${full_backup_sql}" ] || [ ! -f "${datafile_info}" ]; then
        log_error "Param is none canot build backup sql"
        return "${RETURN_INVALID}"
    fi
    log_info "Build datafile sql "
    path_num=`echo "${BACKUP_PATH_LIST}" | sed 's/;/ /g' | wc -w | $MYAWK '{print $1}'`
    index_datafile=0
    echo "    backup as copy incremental level 0 tag 'EBACKUP-${DBNAME}-DATA' " > "${full_backup_sql}"
    while read line; do
        fNo="`echo "$line" | ${MYAWK} '{print $3}'`"
        fsFile="`echo "$line" | ${MYAWK} '{print $4}'`"
        if [ "${LEVEL}" -ne 0 ]; then
            grep "${fsFile}" "${ADDITIONAL}"/dbfiles
            if [ $? -eq 0 ]; then
                continue
            fi
        fi
        index_path=`expr "${index_datafile}" % "${path_num}" + 1`
        index_datafile=`expr "${index_datafile}" + 1`
        path=`echo "${BACKUP_PATH_LIST}" | $MYAWK -F ";" -v path_index="$index_path" '{print $path_index}'`
        create_dir "${path}/${fNo}"
        tmp_str="    (datafile ${fNo} format '${path}/${fNo}/FNO-%f_TS-%N.dbf')"
        has_content="1"
        echo "${tmp_str}" >> "${full_backup_sql}"
    done < "${datafile_info}"
    if [ "${has_content}" -eq "0" ]; then
        delete_file "${full_backup_sql}"
    else
        echo ";" >> "${full_backup_sql}"
    fi
}

build_backup_database_sql()
{
    tmp_sql=$1
    datafile_info=$2
    datafile_minscn=$3
    open_mode=$4
    start_with_spfile_flag=$5
    backup_datafile_sql="${STMP_PATH}/backup_datafile_sql_${PID}.sql"

    build_backup_datafile_sql "${backup_datafile_sql}" "${datafile_info}" 

    dbf_num=`cat "${datafile_info}" | wc -l | $MYAWK '{print $1}'`

    if [ "`rd_substr "${ORA_VERSION}" 1 2`" -gt 12 ]; then
        echo "alter session set events 'trace[krb.*] disk disable, memory disable';" > "${tmp_sql}"
    fi
    echo "configure backup optimization off;" >> "${tmp_sql}"
    echo "configure controlfile autobackup off;" >> "${tmp_sql}"
    echo "set nocfau;" >> "${tmp_sql}"
    echo "configure maxsetsize to unlimited;" >> "${tmp_sql}" 
    if [ "${IS_ENC_BK}" -eq 0 ] && [ "$ORA_PREVERSION" -gt 11 ]; then
        echo "configure encryption for database off;" >> "${tmp_sql}"
    fi
    echo "RUN" >> "${tmp_sql}"
    echo "{" >> "${tmp_sql}"
    echo "    SET COMMAND ID TO 'ProtectAgent_Backup';" >> "${tmp_sql}"
    if [ "$QOS" -gt 0 ]; then
        if [ "${dbf_num}" -ge "${CHANNELS}" ] || [ "${dbf_num}" -eq 0 ]; then
            realQOS=`expr "${QOS}" \* 1024 / "${CHANNELS}"`
        else
            realQOS=`expr "${QOS}" \* 1024 / "${dbf_num}"`
        fi
    fi
    index=1
    if [ "${DBISCLUSTER}" -eq 1 ] && [ "${NODENUMBER}" -ne 0 ]; then
        service_names=`get_service_names "${DBINSTANCE}" "${DBUSER}" "${DBUSERPWD}"`
        service_names=`echo "${service_names}" | $MYAWK -F "," '{print $1}'`
        IPPORTINFO=`echo "$IPPORTINFO" | sed 's/;/ /g' ` 
        while [ "$index" -le "${CHANNELS}" ]; do
            for instance_node_info in ${IPPORTINFO}; do
                host_info=`echo "${instance_node_info}" | $MYAWK -F "," '{print $2}'`
                port_info=`echo "${instance_node_info}" | $MYAWK -F "," '{print $3}'`
                sid_info=`echo "${instance_node_info}" | $MYAWK -F "," '{print $1}'`
                if [ "${QOS}" -eq 0 ]; then
                    echo "    allocate channel eBackup`printf "%02d" "$index"` type disk connect /@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(Host=${host_info})(Port=${port_info}))(CONNECT_DATA=(SERVICE_NAME=${service_names})));" >> "${tmp_sql}"
                else
                    echo "    allocate channel eBackup`printf "%02d" "$index"` type disk rate `printf "%d" "${realQOS}"`k connect /@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(Host=${host_info})(Port=${port_info}))(CONNECT_DATA=(SERVICE_NAME=${service_names})));" >> "${tmp_sql}"
                fi
                index=`expr "$index" + 1`
                if [ "${index}" -gt "${CHANNELS}" ]; then
                    break
                fi
            done
        done
    else
        while [ "$index" -le "${CHANNELS}" ]; do
            if [ -z "${QOS}" ] || [ "${QOS}" -eq 0 ]; then
                echo "    allocate channel eBackup`printf "%02d" "$index"` type disk;" >> "${tmp_sql}"
            else
                echo "    allocate channel eBackup`printf "%02d" "$index"` type disk rate `printf "%d" "${realQOS}"`k;" >> "${tmp_sql}"
            fi
            index=`expr "$index" + 1`
        done
    fi
    echo "    configure device type disk parallelism ${CHANNELS};" >> "${tmp_sql}"
    if [ "${start_with_spfile_flag}" = "true" ]; then
        echo "    backup spfile format '${MAIN_BACKUP_PATH}/spfile.bs' tag 'EBACKUP-${DBNAME}-SPFILE' reuse;" >> "${tmp_sql}"
    fi
    if [  -f "${backup_datafile_sql}" ]; then
        cat "${backup_datafile_sql}" >> "${tmp_sql}"
        delete_file "${backup_datafile_sql}"
    fi
    if [ "${LEVEL}" -eq 1 ] || [ "${LEVEL}" -eq 2 ]; then
        dbf_contents=`ls ${MAIN_BACKUP_PATH}/*/*.dbf`
        log_info "dbf_contents:${dbf_contents}"
        for dbf in ${dbf_contents}; do
          echo "    catalog datafilecopy '${dbf}' tag 'EBACKUP-$DBNAME-DATA' level 0;" >> "${tmp_sql}"
        done
    fi
    if [ "${LEVEL}" -eq 1 ]; then
        echo "    backup incremental level 1 cumulative for recover of copy with tag 'EBACKUP-$DBNAME-DATA' database format '$BACKUP_TEMP/%T_%U';" >> "${tmp_sql}"
        echo "    recover copy of database with tag 'EBACKUP-$DBNAME-DATA';" >> "${tmp_sql}"
        echo "    delete noprompt backup tag 'EBACKUP-$DBNAME-DATA';" >> "${tmp_sql}"
    elif [ "${LEVEL}" -eq 2 ]; then
        echo "    backup incremental level 1 for recover of copy with tag 'EBACKUP-$DBNAME-DATA' database format '$BACKUP_TEMP/%T_%U';" >> "${tmp_sql}"
        echo "    recover copy of database with tag 'EBACKUP-$DBNAME-DATA';" >> "${tmp_sql}"
        echo "    delete noprompt backup tag 'EBACKUP-$DBNAME-DATA';" >> "${tmp_sql}"
    fi
    echo "    DELETE FORCE NOPROMPT ARCHIVELOG like '${MAIN_BACKUP_PATH%/*}/%/log/arch_%';" >> "${tmp_sql}"
    if [ "${open_mode}" != "READONLY" ] && [ "${open_mode}" != "READONLYWITHAPPLY" ] && [ "${open_mode}" != "MOUNTED" ];then
        echo "    sql 'alter system archive log current';" >> "${tmp_sql}"
    fi
    # linux
    echo "change archivelog from scn $datafile_minscn like '/mnt/databackup/Oracle%/log/Database_%_LogRepository%/resetlogs_id%/arch_%.log' unavailable;" >> "${tmp_sql}"
    # aix
    echo "change archivelog from scn $datafile_minscn like '/mnt/databackup/oracle_log%/resetlogs_id%/arch_%.log' unavailable;" >> "${tmp_sql}"

    echo "    backup as copy archivelog from scn $datafile_minscn format '$MAIN_BACKUP_PATH/log/arch_%t_%s.log' reuse;" >> "${tmp_sql}"
    # Oracle 11G RAC 集群的情况下，备份控制文件 as copy 会报错，Oracle的BUG
    if [ "$ORA_PREVERSION" -lt 12 ] && [ "$DBISCLUSTER" -gt 0 ]; then
        echo "    backup current controlfile format '$MAIN_BACKUP_PATH/controlfile.ctl' tag 'EBACKUP-$DBNAME-CTL' reuse;" >> "${tmp_sql}"
    else
        echo "    backup as copy current controlfile format '$MAIN_BACKUP_PATH/controlfile.ctl' tag 'EBACKUP-$DBNAME-CTL' reuse;" >> "${tmp_sql}"
    fi
    echo "}" >> "${tmp_sql}"
}

generate_additional_info()
{
    generate_tmp_file="${STMP_PATH}/generate_additional_info_tmp_${PID}.txt"
    if [ "${ORACLE_SHELLTYPE}" = "csh" ]; then
        set_env_cmd="setenv LANG=c;"
    else
        set_env_cmd="export LANG=c;"
    fi
    su - "${ORA_DB_USER}" -c "echo \"${ORACLE_MARK}\"; ${EXPORT_ORACLE_ENV} ${set_env_cmd} srvctl config database -d ${DBNAME}" > "${generate_tmp_file}" 2>&1
    delete_oracle_mark "${generate_tmp_file}"
    db_pwfile=`cat "${generate_tmp_file}" | grep "Password file" | $MYAWK -F ":" '{print $2}' | $MYAWK '{gsub(/^\s+|\s+$/, "");print}'`
    delete_file "${generate_tmp_file}"
    if [ -z "${db_pwfile}" ]; then
        cp -d -r -p "${IN_ORACLE_HOME}"/dbs/orapw"${DBINSTANCE}" "${ADDITIONAL}"/dbs/orapw"${DBNAME}" >> "${LOG_FILE_NAME}" 2>&1
    elif [ "`rd_substr "${db_pwfile}" 1 1`" = "+" ]; then
        set_env_cmd=`generate_export_grid_oracle_sid "${ASMSIDNAME}"`
        mkdir -p "${BACKUP_TEMP}"/orapwtmp >> "${LOG_FILE_NAME}" 2>&1
        TMP_ORAPW_PATH="${BACKUP_TEMP}"/orapwtmp
        chown -h "${ORA_GRID_USER}":"${ORA_DB_GROUP}" "${TMP_ORAPW_PATH}"
        su - "${ORA_GRID_USER}" -c "${EXPORT_GRID_ENV} ${set_env_cmd} rm -rf ${TMP_ORAPW_PATH}/orapw${DBNAME}; echo pwcopy ${db_pwfile} ${TMP_ORAPW_PATH}/orapw${DBNAME} | asmcmd" >> "${LOG_FILE_NAME}" 2>&1
        log_info "Touch_asm_tag"
        touch "$ADDITIONAL"/dbs/asmtag
        mv "${TMP_ORAPW_PATH}"/orapw"${DBNAME}" "$ADDITIONAL"/dbs >> "${LOG_FILE_NAME}" 2>&1
    else
        cp -d -r -p "${db_pwfile}" "$ADDITIONAL"/dbs/orapw"${DBNAME}" >> "${LOG_FILE_NAME}" 2>&1
    fi

    mkdir -p "${ADDITIONAL}"/netadmin >> "${LOG_FILE_NAME}" 2>&1
    cp -d -r -p "$IN_ORACLE_HOME"/network/admin/* "$ADDITIONAL"/netadmin >> "${LOG_FILE_NAME}" 2>&1
    if [ ${?} -eq 0 ]; then
        touch "$ADDITIONAL"/ok >> "${LOG_FILE_NAME}" 2>&1
    fi
}

move_pfile_to_stmp()
{
    log_info "Get database pfile parameter"
    file_path="${MAIN_BACKUP_PATH}/ebackup-$DBNAME-pfile.ora"
    if [ ! -f "${file_path}" ]; then
        log_error "Error pfile is not exit "
        return "${RETURN_INVALID}"
    fi
    cp -d -f "${file_path}" "${STMP_PATH}"/tempfile_"${PID}" >> "${LOG_FILE_NAME}" 2>&1
    if [ ${?} -ne 0 ]; then
        log_error "copy pfile is error "
        return "${RETURN_INVALID}"
    fi
}

check_backup_level()
{
    # check backup level
    log_info "Check backup level"
    if [ "${LEVEL}" -ne 1 ] && [ "${LEVEL}" -ne 2 ] && [ "${LEVEL}" -ne 0 ]; then
        LEVEL=0
        log_warn "Level is invalid set level=0 full backup"
    fi
    if [ "${LEVEL}" -eq 1 ] || [ "${LEVEL}" -eq 2 ]; then
        if [ -f "${ADDITIONAL}/dbinfo" ]; then
            OLDDBID=`cat "${ADDITIONAL}"/dbinfo | $MYAWK -F ";" '{print $1}'`
            if [ "${DBID}" -ne "$OLDDBID" ]; then
                log_warn "The DBID is different from the last backup, will set level=0 full backup"
                LEVEL=0
            fi
        fi
        if [ ! -f "$ADDITIONAL/first_backup_success" ]; then 
            LEVEL=0
            log_warn "The last full backup not success must do full backup"
        fi
    fi
}

check_backup_archive_log_lost()
{
    instance_name=$1
    db_user=$2
    db_password=$3
    from_scn=$4
    end_scn=$5
    resetlogs_id=$6
    node_list=$7

    tmp_sql="${STMP_PATH}/get_archive_log_list_${PID}.sql"
    tmp_rst="${STMP_PATH}/get_archive_log_rst_${PID}.txt"
    # 检查起始scn和结束scn是否连续，sequence是否连续
    for node in ${node_list}; do
        echo "set linesize 999" > "${tmp_sql}"
        echo "COL name FORMAT a500" >> "${tmp_sql}"
        echo "select to_char(sequence#), to_char(first_change#), to_char(next_change#), name  from v\$archived_log where name is not null \
        and name like '${MAIN_BACKUP_PATH}/log/arch_%' \
        and THREAD#=${node} and RESETLOGS_ID=${resetlogs_id} and (deleted = 'NO') and (ARCHIVED='YES') and (STATUS = 'A') \
        and next_change# > ${from_scn} and first_change# <= ${end_scn} order by first_change#;" >> "${tmp_sql}"
        echo "exit" >> "${tmp_sql}"
        set_db_silence_sql "${tmp_sql}"
        oracle_exe_sql "${db_user}" "${db_password}" "${tmp_sql}" "${tmp_rst}" "${instance_name}" 30 1
        ret_code=$?
        if [ ${ret_code} -ne 0 ]; then
            log_error "CheckLogLost failed, ret=${ret_code}"
            delete_file "${tmp_sql}" "${tmp_rst}" "true"
            return "${ERROR_ORACLE_BACKUP_FAILED}"
        fi
        if [ -s "${tmp_rst}" ]; then
            first_change=`cat "${tmp_rst}" | head -1 | $MYAWK '{print $2}'`
            log_info "first_change=${first_change}, from_scn=${from_scn}."
            if [ "${first_change}" -gt "${from_scn}" ]; then
                log_error "node(${node}) have log lost, backup failed."
                continue
            fi
            next_change=`cat "${tmp_rst}" | tail -1 | $MYAWK '{print $3}'`
            log_info "next_change=${next_change}, end_scn=${end_scn}."
            if [ "${next_change}" -lt "${end_scn}" ]; then
                log_error "node(${node}) have log lost, backup failed."
                continue
            fi
            sequence_list=`cat "${tmp_rst}" | $MYAWK '{print $1}'`
            log_info "sequence_list is ${sequence_list}."
            is_consecutive=`check_list_is_consecutive "${sequence_list}"`
            if [ ${is_consecutive} -ne 1 ]; then
               log_error "check_list_is_consecutive failed"
               continue
            fi
        else
            log_error "node(${node}) have log lost, tmp_rst is null."
            continue
        fi
        log_info "log_is_full."
        delete_file "${tmp_sql}" "${tmp_rst}" "true"
        return 0
    done
    log_error "database have log lost."
    delete_file "${tmp_sql}" "${tmp_rst}" "true"
    return "${ERROR_ORACLE_HAS_LOG_LOST}"
}